<?php
namespace app\api\controller;

use app\api\ApiBaseController;
use app\common\model\UserModel;

class User extends ApiBaseController
{
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * 登录
     * @throws \Exception
     */
    public function login()
    {
        $code = $this->input('code');
        $userInfo = $this->input('userInfo');

        $this->checkParams($code);
        $this->checkParams($userInfo);

        $aResult = $this->mpLogin($code, $userInfo);
        if (!isset($aResult['id'])) {
            exception('登录失败请稍后再试', 500);
        }

        $this->output([
            'token' => $this->encryptToken($aResult['id']),
        ]);
    }

    /**
     * @explain 获取用户信息
     * @throws \Exception
     */
    public function get_wx_info()
    {
        $encryptedData = $this->input('encryptedData');
        $iv = $this->input('iv');
        $this->checkParams([$encryptedData, $iv]);

        $user_info = $this->getUserInfo();
        $session_key = $user_info['session_key'];

        $code = $this->decryptedData($encryptedData, $iv, $session_key, $data);
        if ($code != 0 || empty($data)) {
            exception('获取用户信息失败', $code);
        }

        //更新信息
        $user_info->nickname = $data['nickName'];
        $user_info->avatar = $data['avatarUrl'];
        $user_info->weixin_info = json_encode($data);
        $user_info->update_time = time();
        $user_info->save();

        $this->output();
    }

    /**
     * 小程序登录
     * @param $code
     * @param $userInfo
     * @return array|bool
     * @throws \Exception
     */
    private function mpLogin($code, $userInfo)
    {
        $mpInfo = $this->codeToOpenid($code);
        return UserModel::updateUserInfo($mpInfo['openid'], $userInfo, $mpInfo['session_key']);
    }

    /**
     * 小程序登录（用code获取openid）
     * @param $code
     * @return array 微信接口返回的，有openid和session_key
     * @throws \Exception
     */
    private function codeToOpenid($code)
    {
        $sAppId = 'wxb37187a722512c23';
        $sAppSecret = '35ab986985b260b4d052ebd3f60ba7f2';
        $sUrl = "https://api.weixin.qq.com/sns/jscode2session?appid={$sAppId}&secret={$sAppSecret}&js_code={$code}&grant_type=authorization_code";

        $sJsonResult = file_get_contents($sUrl);
        $aResult = json_decode($sJsonResult, true);
        //$aResult = ['session_key' => 'f9umDYdqoz0wgacCrLDYFA==', 'openid' => 'o4W6G5Fz0M5imbBAIFxSfmPMmKe8'];

        if (empty($aResult)) {
            exception('微信服务器错误，请稍后重试', 600);
        }
        if (isset($aResult['errcode']) && $aResult['errcode'] != 0) {
            exception('登陆失败：' . $aResult['errmsg'], $aResult['errcode']);
        }

        return $aResult;
    }

    /**
     * 检验数据的真实性，并且获取解密后的明文.
     * @param $encryptedData string 加密的用户数据
     * @param $iv string 与用户数据一同返回的初始向量
     * @param $session_key string session_key
     *
     * @param $data object 解密后的数据
     * @return int 成功0，失败返回对应的错误码
     */
    private function decryptedData($encryptedData, $iv, $session_key, &$data)
    {
        $codeArr = $this->decryptedDataCodeArr();

        if (strlen($session_key) != 24) {
            return $codeArr['IllegalAesKey'];
        }
        $aesKey = base64_decode($session_key);

        if (strlen($iv) != 24) {
            return $codeArr['IllegalIv'];
        }
        $aesIV = base64_decode($iv);

        $aesCipher = base64_decode($encryptedData);

        $result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);

        $dataArr = json_decode($result, true);
        if ($dataArr == null) {
            return $codeArr['IllegalBuffer'];
        }
        if ($dataArr['watermark']['appid'] != $this->appid) {
            return $codeArr['IllegalBuffer'];
        }
        $data = $dataArr;
        return $codeArr['OK'];
    }

    /**
     * error code 说明.
     * <ul>
     *    <li>-41001: encodingAesKey 非法</li>
     *    <li>-41003: aes 解密失败</li>
     *    <li>-41004: 解密后得到的buffer非法</li>
     *    <li>-41005: base64加密失败</li>
     *    <li>-41016: base64解密失败</li>
     * </ul>
     */
    private function decryptedDataCodeArr()
    {
        return [
            'OK' => 0,
            'IllegalAesKey' => -41001,
            'IllegalIv' => -41002,
            'IllegalBuffer' => -41003,
            'DecodeBase64Error' => -41004,
        ];
    }
}
